/**
 * Title: jFamilyTree
 * Description: jFamilyTree
 * About: Designed and rewritten by Andreas Lundgren, based on the Treebolic application suite
 * Copyright: (c) 2001-2008
 * Terms of use: see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 */
package jFamilyTree.provider.xml.graph;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import jFamilyTree.model.Model;
import jFamilyTree.model.Settings;
import jFamilyTree.model.Tree;
import jFamilyTree.model.graph.Converter;
import jFamilyTree.model.graph.Graph;
import jFamilyTree.model.graph.MutableGraphNode;
import jFamilyTree.provider.xml.dom.Parser;

/**
 * Graph parser
 * 
 * @author Bernard Bou
 */
public abstract class AbstractParser
{
	// A B S T R A C T . M E T H O D S

	/**
	 * Parse graph from DOM document
	 * 
	 * @return tree if successful
	 */
	abstract protected Graph parseGraph(Document thisDocument);

	/**
	 * Parse settings from DOM document
	 * 
	 * @return graph if successful
	 */
	abstract protected Settings parseSettings(Document thisDocument);

	// P A R S E

	/**
	 * Parse model
	 * 
	 * @return model if successful
	 */
	public Model parseModel(URL thisUrl)
	{
		// DOM document
		Document thisDocument = new Parser().makeDocument(thisUrl, null);
		if (thisDocument == null)
			return null;

		// tree
		Tree thisTree = parseTree(thisDocument);
		if (thisTree == null)
			return null;

		// settings
		Settings theseSettings = parseSettings(thisDocument);

		// model
		return new Model(thisTree, theseSettings);
	}

	/**
	 * Parse tree from url
	 * 
	 * @return tree if successful
	 */
	public Tree parseTree(URL thisUrl)
	{
		// DOM document
		Document thisDocument = new Parser().makeDocument(thisUrl, null);
		if (thisDocument == null)
			return null;

		return parseTree(thisDocument);
	}

	/**
	 * Parse tree from document
	 * 
	 * @return tree if successful
	 */
	private Tree parseTree(Document thisDocument)
	{
		Graph thisGraph = parseGraph(thisDocument);
		return new Converter<MutableGraphNode>().graphToTree(thisGraph);
	}

	// H E L P E R S

	/**
	 * Find DOM element with given tag
	 * 
	 * @param thisElement
	 *        starting DOM element
	 * @param thisTagName
	 *        tag
	 * @return DOM element if found, null if none
	 */
	static protected Element getFirstElementByTagName(Element thisElement, String thisTagName)
	{
		NodeList thisList = thisElement.getElementsByTagName(thisTagName);
		if (thisList.getLength() > 0)
			return (Element) thisList.item(0);
		return null;
	}

	/**
	 * Find DOM element with given tag among first level children
	 * 
	 * @param thisElement
	 *        starting DOM element
	 * @param thisTagName
	 *        tag
	 * @return DOM element if found, null if none
	 */
	static protected Element getFirstLevel1ElementByTagName(Element thisElement, String thisTagName)
	{
		List<Element> theseChildElements = getLevel1ChildElementsByTagName(thisElement, thisTagName);
		if (!theseChildElements.isEmpty())
			return theseChildElements.get(0);
		return null;
	}

	/**
	 * Find DOM elements with given tag among first level children
	 * 
	 * @param thisElement
	 *        starting DOM element
	 * @param thisTagName
	 *        tag
	 * @return DOM element if found, null if none
	 */
	static protected List<Element> getLevel1ChildElementsByTagName(Element thisElement, String thisTagName)
	{
		ArrayList<Element> thisList = new ArrayList<Element>();
		NodeList theseChildren = thisElement.getChildNodes();
		for (int i = 0; i < theseChildren.getLength(); i++)
		{
			Node thisNode = theseChildren.item(i);
			if (thisNode instanceof Element)
			{
				Element thisChildElement = (Element) thisNode;
				if (thisChildElement.getTagName().equals(thisTagName))
					thisList.add(thisChildElement);
			}
		}
		return thisList;
	}
}
